﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QLabValues;
using AtomicTypes;

namespace NumericalCalculations
{
    public static class Helper
    {
        public static  string PrintEnumName(IQLabValueType type)
        {
            switch (type)
            {
                case IQLabValueType.QInt64NDimArray:
                    return "'int64'";
           
                case IQLabValueType.QInt32NDimArray:
                    return "'int32'";
                    
                case IQLabValueType.QInt16NDimArray:
                    return "'int16'";

                case IQLabValueType.QInt8NDimArray:
                    return "'int8'";

                case IQLabValueType.QUInt64NDimArray:
                    return "'uint64'";

                case IQLabValueType.QUInt32NDimArray:
                    return "'uint32'";

                case IQLabValueType.QUInt16NDimArray:
                    return "'uint16'";

                case IQLabValueType.QUInt8NDimArray:
                    return "'uint8'";
                
                case IQLabValueType.QFloat64NDimArray:
                    return "'double'";

                case IQLabValueType.QFloat32NDimArray:
                    return "'single'";
                
                case IQLabValueType.QCharNDimArray:
                    return "'char'";

                case IQLabValueType.QBooleanNDimArray:
                    return "'logical'";

                case IQLabValueType.QCell:
                    return "'cell'";

                case IQLabValueType.QFunctionHandle:
                    return "'function_handle'";

                case IQLabValueType.QStructValueNDimArray:
                    return "'struct'";

                default:
                    return "error";
            }




        }




        //metod od liste IQLabValuesa vraca listu odgovarajucih enumova- U OVOM TRENUTKU NE TREBA
        //JER SAM NAPRAVIO DONJI METOD TAKO DA NE PRIHVATA ENUM, ALI CE MOZDA ZATREBATI NEKAD
        public static List<IQLabValueType> getEnumList(List<IQLabValue> o)
        {
            List<IQLabValueType> res = new List<IQLabValueType>();

            foreach (var curr in o)
            {
                res.Add(curr.GetEnumType());
            }

            return res;
        }




        //metod vraca da li prvi tip moze da se konvertuje u drugi
        //u obzir se uzima da mogu da se dobiju samo naveci moguci tipovi svoje vrste -> Boolean, Char, UInt64, Int64, Float64
        //hijerarhija je sledeca: Boolean, Char, UInt64, Int64, Float64 (boolean i char su na istom nivou hijerarhije)
        //moguce je konvertovati samo nize u vise
        public static bool canConvertToEnum(IQLabValue first, IQLabValueType second)
        {
            if (second == IQLabValueType.QBooleanNDimArray)
            {
                if (first.GetEnumType() == IQLabValueType.QBooleanNDimArray)
                    return true;
            }
            else if (second == IQLabValueType.QCharNDimArray)
            {
                if (first.GetEnumType() == IQLabValueType.QCharNDimArray)
                    return true;
            }
            else if (second == IQLabValueType.QUInt64NDimArray)
            {
                IQLabValueType enumHelp = first.GetEnumType();
                if ((enumHelp == IQLabValueType.QCharNDimArray) ||
                    (enumHelp == IQLabValueType.QBooleanNDimArray) ||
                    (enumHelp == IQLabValueType.QUInt64NDimArray))
                    return true;
            }
            else if (second == IQLabValueType.QInt64NDimArray)
            {
                IQLabValueType enumHelp = first.GetEnumType();
                if ((enumHelp == IQLabValueType.QBooleanNDimArray) ||
                    (enumHelp == IQLabValueType.QCharNDimArray) ||
                    (enumHelp == IQLabValueType.QUInt64NDimArray) ||
                    (enumHelp == IQLabValueType.QInt64NDimArray))
                    return true;
            }
            else if (second == IQLabValueType.QFloat64NDimArray)
            {
                IQLabValueType enumHelp = first.GetEnumType();
                if ((enumHelp == IQLabValueType.QBooleanNDimArray) ||
                    (enumHelp == IQLabValueType.QCharNDimArray) ||
                    (enumHelp == IQLabValueType.QUInt64NDimArray) ||
                    (enumHelp == IQLabValueType.QInt64NDimArray) ||
                    (enumHelp == IQLabValueType.QFloat64NDimArray))
                    return true;
            }

            return false;


        }


        //Metod koji kaze moze li se svaki element liste konvertovati u neki odredjeni enum
        // koristicemo ga kod fja kako bi dobili kog ce tipa biti rezultat
        //provera ce se vrsiti, tako sto se proverava gornja hijerarhija tipova
        public static bool canConvertListToEnum(List<IQLabValue> l, IQLabValueType t)
        {
            bool ind = true;
            foreach (var el in l)
            {
                if (canConvertToEnum(el, t) == false)
                {
                    ind = false;
                    break;
                }
            }
            return ind;
        }

        //proverava da li su istih dimenzija prosledjeni argumenti
        //ovi metodi ce se pozivati nakon konvertovanja
        //dole su overloadovani za sve moguce argumente koje moze da dobije
        public static bool checkDimensions(QInt64NDimArray first, QInt64NDimArray second)
        {
            int[] fd = (int[])first.DimensionSize.Clone();
            int[] sd = (int[])second.DimensionSize.Clone();

            if (fd.Count() != sd.Count())
                return false;
            for (int i = 0; i < fd.Count(); i++)
                if (fd[i] != sd[i])
                    return false;
            return true;
        }

        public static bool checkDimensions(QBooleanNDimArray first, QBooleanNDimArray second)
        {
            int[] fd = (int[])first.DimensionSize.Clone();
            int[] sd = (int[])second.DimensionSize.Clone();

            if (fd.Count() != sd.Count())
                return false;
            for (int i = 0; i < fd.Count(); i++)
                if (fd[i] != sd[i])
                    return false;
            return true;
        }

        public static bool checkDimensions(QUInt64NDimArray first, QUInt64NDimArray second)
        {
            int[] fd = (int[])first.DimensionSize.Clone();
            int[] sd = (int[])second.DimensionSize.Clone();

            if (fd.Count() != sd.Count())
                return false;
            for (int i = 0; i < fd.Count(); i++)
                if (fd[i] != sd[i])
                    return false;
            return true;
        }

        public static bool checkDimensions(QFloat64NDimArray first, QFloat64NDimArray second)
        {
            int[] fd = (int[])first.DimensionSize.Clone();
            int[] sd = (int[])second.DimensionSize.Clone();

            if (fd.Count() != sd.Count())
                return false;
            for (int i = 0; i < fd.Count(); i++)
                if (fd[i] != sd[i])
                    return false;
            return true;
        }


        public static bool checkDimensions(QCharNDimArray first, QCharNDimArray second)
        {
            int[] fd = (int[])first.DimensionSize.Clone();
            int[] sd = (int[])second.DimensionSize.Clone();

            if (fd.Count() != sd.Count())
                return false;
            for (int i = 0; i < fd.Count(); i++)
                if (fd[i] != sd[i])
                    return false;
            return true;
        }

        public static bool checkDimensions(QCell first, QCell second)
        {
            int[] fd = (int[])first.DimensionSize.Clone();
            int[] sd = (int[])second.DimensionSize.Clone();

            if (fd.Count() != sd.Count())
                return false;
            for (int i = 0; i < fd.Count(); i++)
                if (fd[i] != sd[i])
                    return false;
            return true;
        }

        //proverava da li je prosledjena vrednost skalar PAZNJA!!!
        // PAZNJA: ne radi za objekte napravljene konstruktorom koji prima samo niz dimenzija
        public static bool checkIfScalar(IQLabValue iq)
        {
            if (canConvertToEnum(iq, IQLabValueType.QFloat64NDimArray))
            {
                QFloat64NDimArray qf = new QFloat64NDimArray(iq);
                if (qf.Dimension > 2)
                    return false;
                if ((qf.DimensionSize[0] != 1) || (qf.DimensionSize[1] != 1))
                    return false;
                return true;
            }
            else
                return false;
        }

        //uvek cu proveravati da li je cell pre nego sto pozovem
        public static bool checkIfCellIsScalar(IQLabValue iq)
        {
             IQLabValueType en = iq.GetEnumType();

             if (en == IQLabValueType.QCell)
             {
                 QCell qc = (QCell)iq;
                 if (qc.Dimension > 2)
                     return false;
                 if ((qc.DimensionSize[0] != 1) || (qc.DimensionSize[1] != 1))
                     return false;
                 return true;
             }
             else
                 return false;
        }


        public static bool checkIfStructArrayIsScalar(IQLabValue iq)
        {
            IQLabValueType en = iq.GetEnumType();

            if (en == IQLabValueType.QStructValueNDimArray)
            {
                QStructValueNDimArray qc = (QStructValueNDimArray)iq;
                if (qc.Dimension > 2)
                    return false;
                if ((qc.DimensionSize[0] != 1) || (qc.DimensionSize[1] != 1))
                    return false;
                return true;
            }
            else
                return false;
        }

        //ova funkcija ce biti pozivana kada lista ne moze da se konvertuje u neki tip, npr qfloat64array
        //treba da vrati string koji odgovara imenu tipa argumenta koji je prvi koji ne moze da se konvertuje
        //bice pozivana samo nakon sto canConvertListToEnum vrati false
        public static IQLabValueType TypeOfThatCouldNotConvert(List<IQLabValue> list, IQLabValueType type)
        {
            foreach (var el in list)
            {
                if (canConvertToEnum(el, type) == false)
                {
                    return el.GetEnumType();
                }
            }

            //posto ce se pozivati uvek nakon provere koja je bila neupesna
            //ovo nikad ne bi trebala da vrati
            throw (new NumericalCalculationsException("This should not be happening"));
        }


        //proverava da li su dve IQlabValues vrednosti iste
        //koristi se u donjoj funkciji koja proverava da li su liste iste
        //proverava samo za numericke vrednosti(qfloa64arry ...), u suprotnom(cell, struct...) vraca po defaultu false
        public static bool areIQlabValueEqual(IQLabValue first, IQLabValue second)
        {
            if (first.GetEnumType() != second.GetEnumType())
                return false;
            if (canConvertToEnum(first, IQLabValueType.QFloat64NDimArray))
            {
                //ukoliko moze da se konvertuje i prvi, moze i drugi jer su istog tipa
                QFloat64NDimArray qf1 = new QFloat64NDimArray(first);
                QFloat64NDimArray qf2 = new QFloat64NDimArray(second);

                //ukoliko nisu istih dimenzija -> false
                if (checkDimensions(qf1, qf2) == false)
                    return false;
                //provervmo redom da li su im jednaki elementi
                for (int i = 0; i < qf1.Data.Length; i++)
                {
                    double x1 = qf1.Data[i].Real;
                    double x2 = qf2.Data[i].Real;
                    double y1 = qf1.Data[i].Imag;
                    double y2 = qf2.Data[i].Imag;

                    if (double.IsNaN(x1) && double.IsNaN(x2))
                        if (double.IsNaN(y1) && double.IsNaN(y2))
                            continue;
                        else if (y1 == y2)
                            continue;

                    if (double.IsNaN(y1) && double.IsNaN(y2))
                         if (x1 == x2)
                            continue;
                    
                    if ((x1 != x2) || (y1 != y2))
                        return false;
                }

                //jednaki su im tipovi, dimenzije i podaci
                return true;
            }
            else
            {
                //inace, ako je cell
                if (first.GetEnumType() == IQLabValueType.QCell)
                {
                    QCell cf = (QCell)first;
                    QCell cs = (QCell)second;

                    if (cf.Dimension != cs.Dimension)
                        return false;

                    for (int i = 0; i < cf.Dimension; i++)
                        if (cf.DimensionSize[i] != cs.DimensionSize[i])
                            return false;
                    
                    if (cf.Data.Length != cs.Data.Length)
                        return false;

                    bool p = true;
                    for (int i = 0; i < cf.Data.Length; i++)
                        p = p && areIQlabValueEqual(cf.Data[i], cs.Data[i]);
                    return p;
                }

                //inace - trenutno nam nista drugo ne treba
                return false;
            }
        }


        //proverava da li su dve liste IQlabValue iste
        //koristicu je u testovima(NumericalCalculationsTest), gde cu pomocu nje uporedjivati
        //ono sto mi vraca funkvija i ono sto bi trebala da vrati
        public static bool areIQlabValueListsEqual(List<IQLabValue> list1, List<IQLabValue> list2)
        {
            if (list1.Count != list2.Count)
                return false;
            else
            {
                //procicemo kroz liste trazeci nepoklapanje
                for (int i = 0; i < list1.Count; i++)
                {
                    if (areIQlabValueEqual(list1.ElementAt(i), list2.ElementAt(i)) == false)
                        return false;
                }

                //prosli smo kroz celu listu i svi su se poklapali
                return true;
            }
        }

        //proverava da li IQlabValue ima u sebi kompleksnu vrednost
        //uvek bi trebao da se poziva posle canConvertToEnum za qfloat64
        public static bool isIQLabValueComplex(IQLabValue iq)
        {

            QFloat64NDimArray qfa = new QFloat64NDimArray(iq);
            for (int i = 0; i < qfa.Data.Length; i++)
                if (qfa.Data[i].IsComplex())
                    return true;
            return false;

        }

        //pretpostavlja se da se prosledjuje Qfloat64NdimArray
        //za potrebe testiranja, zato sto matlab vraca rezultat na 4 decimale, a nas program racuna na 15
        public static IQLabValue roundTo4Decimals(IQLabValue o)
        {
            if (o.GetEnumType() == IQLabValueType.QFloat64NDimArray)
            {
                QFloat64NDimArray qf = new QFloat64NDimArray(o);

                QFloat64[] vals = new QFloat64[qf.Data.Count()];
                for (int i = 0; i < vals.Count(); i++)
                {
                    double x1;
                    double y1;
                    //zaokruzimo realan deo
                    if (double.IsInfinity(qf.Data[i].Real) || (qf.Data[i].Real == double.NaN))
                        x1 = qf.Data[i].Real;
                    else
                        x1 = Math.Round(qf.Data[i].Real, 4);
                    //zaokruzimo imaginaran deo
                    if (double.IsInfinity(qf.Data[i].Imag) || (qf.Data[i].Imag == double.NaN))
                        y1 = qf.Data[i].Imag;
                    else
                        y1 = Math.Round(qf.Data[i].Imag, 4);
                    vals[i] = new QFloat64(x1, y1);
                }

                QFloat64NDimArray resQf = new QFloat64NDimArray(vals, qf.DimensionSize);
                return resQf;
            }
            else
                return null;
        }


        //koristeci prethodni metod, celu listu zaokruzava na 4 decimale
        //prolazi kroz listu, i ako naidje na qfloat64, njega ce zaokruziti, i ubaciti u rezultat zaokruzenog
        //ako naidje na nesto drugo, nece ga menjati vec ce ga ubaciti istog
        public static List<IQLabValue> roundListTo4Decimals(List<IQLabValue> o)
        {
            List<IQLabValue> res = new List<IQLabValue>();
            foreach (var el in o)
            {
                if (el.GetEnumType() == IQLabValueType.QFloat64NDimArray)
                {
                    res.Add(roundTo4Decimals(el));
                }
                else
                {
                    res.Add(el);
                }

            }
            return res;
        }


        //ignorise imaginarne
        public static bool checkIfRealIsInt(QFloat64 qf)
        {
            return qf.Real == ((long)qf.Real);
        }

        //koristicu kod bitcmpa i slicnih funkcija da proverim da li je drugi elemnt koji oznacava broj bitova potrebnih za zapis
        // dovoljno veliki da se zapise vrednost prvog argumenta
        public static bool checkAreBitsEnough(QUInt64 qui, QUInt64 nbits)
        {
            if (qui.Real < Math.Pow(2, nbits.Real))
                return false;
            else return true;
        }

        
        //pozivacu ovaj metod uvek kad proverim da qcharndimaaray nema vise od dv2 dimenzije
        //za dvodimenzionalni qcharndimarray vraca listu stringova koji odgovaraju redovima
        public static List<string> QCharNDimArrayToStringList(QCharNDimArray qc)
        {
            //da li sam dobio niz stringova
            if (qc.Data != null && qc.Dimension == 2)
            {
                List<string> res = new List<string>();
                int i = qc.DimensionSize[0]; // ovo mi kazuje koliko ce elemenata biti
                int j = qc.DimensionSize[1]; // ovo mi kazuje duzinu stringa

                for (int k = 0; k < i; k++)
                {
                    char[] car = new char[j];
                    for (int l = 0; l < j; l++)
                        car[l] = qc[k,l].Real;
                    string ns = new string(car);
                    res.Add(ns);
                }

                return res;
            }
            else
                return null; // ovo ne bi nikad treblao da vrati
        }

        //za dobijenu listu stringova kreira dvodimenzionalni qcharndimarray
        public static QCharNDimArray StringListToQCharNDimArray(List<string> s)
        { 
            //pornalazim maks. velicinu, to ce biti druga dimenzija 
            int maxlength = 0;
            foreach (string el in s)
            {
                if (el.Length > maxlength)
                    maxlength = el.Length;
            }

            int first = s.Count;
            int second = maxlength;

            QCharNDimArray qc = new QCharNDimArray(first, second);

            for (int i=0;  i< first; i++)
            {
                char[] cs = s.ElementAt(i).ToCharArray();
                int j = 0;
                for (;j<s.ElementAt(i).Length;j++)
                    qc[i,j] = new QChar(cs[j]);

                while (j<s.ElementAt(i).Length)
                {
                    qc[i,j] = new QChar('\0');
                    j++;
                }

            }

            return qc;
        }


        public static bool IsString(QCharNDimArray qc)
        {
            if (qc.Dimension > 2)
                return false;
            if (qc.DimensionSize[0] != 1)
                return false;
            return true;
        }

        
        public static bool IsVector(QFloat64NDimArray m)
        {
            if (m.Dimension > 2)
                return false;
            if (m.DimensionSize[0] != 1 && m.DimensionSize[1] != 1)
                return false;
            return true;
        }
    

        public static bool AreQCharNDimArraysEaqual(QCharNDimArray fqc, QCharNDimArray sqc)
        {
            if (Helper.checkDimensions(fqc, sqc) == false)
                return false;

            for (int i = 0; i < fqc.Data.Length; i++)
                if (fqc.Data[i].Real != sqc.Data[i].Real)
                    return false;

            return true;
        }

        //pomocna fja za poredjenje kompleksnih brojeva koja radi i za nekompleksne brojeve
        //vraca true ako je prvi manji ili jednak, false inace
        //koristi se u funkciji issorted
        public static bool lessThanOrEqualComplex(double x1, double y1, double x2, double y2)
        {
            //PAZNJA: u ovom poredjenju NaN je najveci! Veci je i od beskonacnosti!!!
            //ako je drugi argument NaN on je uvek >= od prvog
            if (double.IsNaN(x2) || double.IsNaN(y2))
                return true;
            
            //ovde drugi argument nije NaN!
            //ako je prvi NaN a drugi nije NaN onda vracamo false
            if (double.IsNaN(x1) || double.IsNaN(y1))
                return false;

            //radi se o nekompleksnim brojevima (standardno poredjenje)
            if (y1 == 0 && y2 == 0)
            {
                if (x1 <= x2)
                    return true;

                return false;
            }
            //inace bar jedan je kompleksni broj
            // Matlab poredi dva kompleksna broja tako sto im najpre poredi moduo, a ako je moduo isti poredi im argument (ugao)
            double moduo1 = Math.Sqrt(x1 * x1 + y1 * y1);
            double moduo2 = Math.Sqrt(x2 * x2 + y2 * y2);

            if (moduo1 < moduo2)
                return true;
            else if (moduo1 > moduo2)
                return false;
            else
            {
                //moduli su jednaki, poredjenje argumenata (uglova)
                double argument1, argument2;
                if (y1 == 0)
                    argument1 = x1 < 0 ? Math.PI : 0;
                else if (x1 == 0)
                    argument1 = y1 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument1 = Math.Atan(y1 / x1);

                if (y2 == 0)
                    argument2 = x2 < 0 ? Math.PI : 0;
                else if (x2 == 0)
                    argument2 = y2 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument2 = Math.Atan(y2 / x2);

                if (argument1 <= argument2)
                    return true;

                 return false;
            }
        }

        //pomocna fja za poredjenje kompleksnih brojeva koja radi i za nekompleksne brojeve
        //vraca true ako je prvi manji, false inace
        //koristi se u funkciji sort
        public static bool lessThanComplex(double x1, double y1, double x2, double y2)
        {
            //PAZNJA: u ovom poredjenju NaN je najveci! Veci je i od beskonacnosti!!!
            //ako je prvi argument NaN on je uvek >= od drugog, tj. nikada nije manji pa vracamo false
            if (double.IsNaN(x1) || double.IsNaN(y1))
                return false;

            //sada prvi argument nije NaN!
            //ako je drugi argument NaN a prvi nije, vracamo true
            if (double.IsNaN(x2) || double.IsNaN(y2))
                return true;

            //radi se o nekompleksnim brojevima (standardno poredjenje)
            if (y1 == 0 && y2 == 0)
            {
                if (x1 < x2)
                    return true;

                return false;
            }

            //inace bar jedan je kompleksni broj
            // Matlab poredi dva kompleksna broja tako sto im najpre poredi moduo, a ako je moduo isti poredi im argument (ugao)
            double moduo1 = Math.Sqrt(x1 * x1 + y1 * y1);
            double moduo2 = Math.Sqrt(x2 * x2 + y2 * y2);

            if (moduo1 < moduo2)
                return true;
            else if (moduo1 > moduo2)
                return false;
            else
            {
                //moduli su jednaki, poredjenje argumenata (uglova)
                double argument1, argument2;
                if (y1 == 0)
                    argument1 = x1 < 0 ? Math.PI : 0;
                else if (x1 == 0)
                    argument1 = y1 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument1 = Math.Atan(y1 / x1);

                if (y2 == 0)
                    argument2 = x2 < 0 ? Math.PI : 0;
                else if (x2 == 0)
                    argument2 = y2 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument2 = Math.Atan(y2 / x2);

                if (argument1 < argument2)
                    return true;

                return false;
            }
        }

        //pomocna fja za poredjenje kompleksnih brojeva koja radi i za nekompleksne brojeve
        //vraca true ako je prvi veci, false inace
        //koristi se u funkciji sort
        public static bool greaterThanComplex(double x1, double y1, double x2, double y2)
        {
            //PAZNJA: u ovom poredjenju NaN je najveci! Veci je i od beskonacnosti!!!
            //ako je drugi argument NaN on je uvek >= od prvog
            if (double.IsNaN(x2) || double.IsNaN(y2))
                return false;

            //sada drugi argument nije NaN!
            //ako je prvi argument NaN a drugi nije, vracamo true
            if (double.IsNaN(x1) || double.IsNaN(y1))
                return true;

            //radi se o nekompleksnim brojevima (standardno poredjenje)
            if (y1 == 0 && y2 == 0)
            {
                if (x1 > x2)
                    return true;

                return false;
            }

            //inace bar jedan je kompleksni broj
            // Matlab poredi dva kompleksna broja tako sto im najpre poredi moduo, a ako je moduo isti poredi im argument (ugao)
            double moduo1 = Math.Sqrt(x1 * x1 + y1 * y1);
            double moduo2 = Math.Sqrt(x2 * x2 + y2 * y2);

            if (moduo1 > moduo2)
                return true;
            else if (moduo1 < moduo2)
                return false;
            else
            {
                //moduli su jednaki, poredjenje argumenata (uglova)
                double argument1, argument2;
                if (y1 == 0)
                    argument1 = x1 < 0 ? Math.PI : 0;
                else if (x1 == 0)
                    argument1 = y1 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument1 = Math.Atan(y1 / x1);

                if (y2 == 0)
                    argument2 = x2 < 0 ? Math.PI : 0;
                else if (x2 == 0)
                    argument2 = y2 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument2 = Math.Atan(y2 / x2);

                if (argument1 > argument2)
                    return true;

                return false;
            }
        }

        //pomocna fja za poredjenje kompleksnih brojeva koja radi i za nekompleksne brojeve
        //vraca true ako je prvi veci ili jednak od drugog argumenta, false inace
        public static bool greaterThanOrEqualComplex(double x1, double y1, double x2, double y2)
        {
            //PAZNJA: u ovom poredjenju NaN je najveci! Veci je i od beskonacnosti!!!
            //ako je prvi argument NaN on je uvek >= od prvog
            if (double.IsNaN(x1) || double.IsNaN(y1))
                return true;

            //sada prvi argument nije NaN!
            //ako je drugi argument NaN a prvii nije, vracamo false
            if (double.IsNaN(x1) || double.IsNaN(y1))
                return false;

            //radi se o nekompleksnim brojevima (standardno poredjenje)
            if (y1 == 0 && y2 == 0)
            {
                if (x1 >= x2)
                    return true;

                return false;
            }

            //inace bar jedan je kompleksni broj
            // Matlab poredi dva kompleksna broja tako sto im najpre poredi moduo, a ako je moduo isti poredi im argument (ugao)
            double moduo1 = Math.Sqrt(x1 * x1 + y1 * y1);
            double moduo2 = Math.Sqrt(x2 * x2 + y2 * y2);

            if (moduo1 > moduo2)
                return true;
            else if (moduo1 < moduo2)
                return false;
            else
            {
                //moduli su jednaki, poredjenje argumenata (uglova)
                double argument1, argument2;
                if (y1 == 0)
                    argument1 = x1 < 0 ? Math.PI : 0;
                else if (x1 == 0)
                    argument1 = y1 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument1 = Math.Atan(y1 / x1);

                if (y2 == 0)
                    argument2 = x2 < 0 ? Math.PI : 0;
                else if (x2 == 0)
                    argument2 = y2 < 0 ? 3 * Math.PI / 2 : Math.PI / 2;
                else
                    argument2 = Math.Atan(y2 / x2);

                if (argument1 >= argument2)
                    return true;

                return false;
            }
        }

        //pomocna funkcija koja za date koordinate vraca odgovarajucu poziciju
        public static int CoordinatesToPosition(int[] DimensionSize, params int[] coordinates)
        {
            int[] newCoords;
            int[] newDimSize;

            //ako ima vise dimenzija nego koordinata, koordinate se prosiruju 0 ([2,1,3] --> [2,1,3,0,0])
            if (coordinates.Length < DimensionSize.Length)
            {
                newCoords = new int[DimensionSize.Length];
                //prepisujemo prosledjene koordinate, a ostale se dobijaju iz poslednje
                //npr ako je data matrica A dimenzija 2x3x2 izraz A(0,4) je ekvivalentan sa A(0,1,1) jer se trazi element
                //u nultoj vrsti i cetvrtoj koloni a cetvrta kolona se nalazi u prvoj ravni matrice A kao prva kolona u toj ravni
                int i;
                for (i = 0; i < coordinates.Length - 1; i++)
                    newCoords[i] = coordinates[i];

                int t = coordinates[coordinates.Length - 1];
                for (; i < newCoords.Length; i++)
                {
                    newCoords[i] = t % DimensionSize[i];
                    t = t / DimensionSize[i];
                }

                coordinates = newCoords;
            }

            //ako ima vise koordinata nego dimenzija, dimenzije se prosiruju jedinicama (2x3x2 --> 2x3x2x1x1...)
            if (DimensionSize.Length < coordinates.Length)
            {
                newDimSize = new int[coordinates.Length];
                //prepisujemo stare dimenzije, a ostale su jednake 1
                for (int i = 0; i < DimensionSize.Length; i++)
                    newDimSize[i] = DimensionSize[i];
                for (int i = DimensionSize.Length; i < newDimSize.Length; i++)
                    newDimSize[i] = 1;

                DimensionSize = newDimSize;
            }

            //provera regularnosti dimenzija
            for (int i = 0; i < coordinates.Length; i++)
                if (coordinates[i] >= DimensionSize[i] || coordinates[i] < 0)
                    throw (new NumericalCalculationsException("Invalid coordinates."));

            int Dimension = DimensionSize.Length;
            int retval = coordinates[Dimension - 1];

            for (int i = Dimension - 2; i > -1; i--)
                retval = retval * DimensionSize[i] + coordinates[i];

            return retval;
        }

        //pomocna funkcija koja za datu poziciju vraca odgovarajuce koordinate
        public static int[] PositionToCoordinates(int[] DimensionSize, int position)
        {
            int prod = 1;
            for (int i = 0; i < DimensionSize.Length; i++)
                prod *= DimensionSize[i];

            if (position < 0 || position >= prod)
                throw (new NumericalCalculationsException("Invalid position."));

            int Dimension = DimensionSize.Length;
            var coordinates = new int[Dimension];

            var rest = position;

            for (int i = 0; i < Dimension; i++)
            {
                coordinates[i] = rest % DimensionSize[i];
                rest = rest / DimensionSize[i];
            }

            return coordinates;
        }

        //pomocna fja koja se koristi u funkciji 'filter'
        //racuna neko kasnjenje nekakvih kola (tj. digitalnog filtera) ili tako nesto... :)
        public static QFloat64 CalculateZ(QFloat64[] x, QFloat64[] y, QFloat64[] z, QFloat64[] b, QFloat64[] a, int i, int m, int n)
        {
            if (i > n - 1)
                return new QFloat64(0, 0);
            if (m < 0)
                return new QFloat64(z[i].Real * a[0].Real - z[i].Imag * a[0].Imag, z[i].Real * a[0].Imag - z[i].Imag * a[0].Real);

            double bR, bI, aR, aI;
            if(i + 1 > b.Length - 1)
            {
                bR = 0;
                bI = 0;
            }
            else
            {
                bR = b[i + 1].Real;
                bI = b[i + 1].Imag;
            }

            if(i + 1 > a.Length - 1)
            {
                aR = 0;
                aI = 0;
            }
            else
            {
                aR = a[i + 1].Real;
                aI = a[i + 1].Imag;
            }

            QFloat64 t = CalculateZ(x, y, z, b, a, i + 1, m - 1, n);

            double Real = bR * x[m].Real - bI * x[m].Imag - (aR * y[m].Real - aI * y[m].Imag) + t.Real;
            double Imag = bR * x[m].Imag + bI * x[m].Real - (aR * y[m].Imag - aI * y[m].Real) + t.Imag;

            return new QFloat64(Real, Imag);
        }
    }
}
